Skip to content

sync: merge upstream/main (2026-07-21) — NimBLE 1.10, picolibc, 78 commits#73

Merged
Coral-coder merged 87 commits into
mainfrom
sync/upstream-2026-07-21
Jul 23, 2026
Merged

sync: merge upstream/main (2026-07-21) — NimBLE 1.10, picolibc, 78 commits#73
Coral-coder merged 87 commits into
mainfrom
sync/upstream-2026-07-21

Conversation

@Coral-coder

Copy link
Copy Markdown
Owner

The largest sync since the fork reconciled — 78 upstream commits.

What upstream brought

  • NimBLE 1.10.0 (our pin carried zero fork commits — plain fast-forward) + Mbed TLS 3.6.7 (new submodule) + fresh SiFli HAL/LCPU patch (incl. nested BT middleware bump)
  • picolibc as default libc; in-tree libc removed (meson + ninja are new build deps)
  • Public header moves under include/pbl (drivers, logging); subsystem concept introduced
  • Power-mode/cpumode subsystem removed — our Debugging menu drops its Power Mode row accordingly (fork governor was already inert)
  • BLE: reconnect-advertising backoff during churn, sooner post-connect parameter updates, AMS CSV tolerance, PPoG alloc-failure logging
  • lsm6dso FIFO burst reads, speaker DMA throttle + volume rework, notification storage leak fixes, sleep-summary-once-per-night, PRF committed before factory-reset wipe, docs overhaul

Reconciliation highlights (details in the merge commit)

  • gap_le_slave_reconnect: fork's dual-phone term selection composed with upstream's churn backoff (backoff applies to cold reconnects only)
  • ppogatt: upstream's new log adapted from their transport role to our phone slot; reversed-PPoG stays removed
  • Fork-only files updated for the header moves
  • Audited all 78 commits' additions against the merged tree — the only absent lines are the deliberate reversed-PPoG deletion and the role→slot adaptation

Verification

  • Full unit test suite green (with correct nested-submodule pins)
  • obelix normal builds both slots (slot-diff 27,961 bytes = healthy per-slot linking); PRF builds clean
  • On-wrist: normal_obelix_pvt_v4.26.0-400-ge1b6639ae.pbz delivered — BT foundations moved (NimBLE 1.10 + new HAL), so dual-phone/blooping needs a deliberate wrist check; -302 remains the fallback

Merge with a merge commit — do not squash.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CNRhmmV8Cg7o6U4BBXkkx7


Generated by Claude Code

jplexer and others added 30 commits July 20, 2026 16:54
The DAC-path volume was converted from the 0-100 UI scale as
(vol - 36) * 2 half-dB, i.e. -36..+64 dB. Any volume above 36 applied
positive digital gain, which hard-clips full-scale content: alarm tones
(volume 60, +24 dB) played as a digital-full-scale square wave, at
maximum loudness regardless of the speaker volume preference, and
overdriving the speaker well past the level the factory test validates.
Volumes above 90 exceeded the HAL's +54 dB limit, so
HAL_AUDCODEC_Config_DACPath_Volume failed without writing the gain
registers and the error was ignored, leaving the 0 dB baseline and
making the volume curve non-monotonic (90 was louder than 100).

The mapping had been dead code until 0e2afbd started re-applying the
volume after DAC path setup (v4.20.0), which is when alarms became
loud and distorted in the field.

Map 1..100 linearly to -36..0 dB instead (0 still mutes), and log a
warning if the HAL rejects the value.

Fixes HARD-711

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Joshua Jun <lets@throw.rocks>
With the SF32LB volume mapping fixed to attenuation-only, the previous
constant of 60 now really means -14.4 dB instead of the clipped +24 dB
it produced before, which would make alarms noticeably quiet. Play
alarms at volume 100 (0 dB DAC gain) - the loudest undistorted level
and the same drive level the factory speaker test runs at. The user's
global speaker volume preference scales alarms down from there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Joshua Jun <lets@throw.rocks>
Signed-off-by: Joshua Jun <lets@throw.rocks>
Signed-off-by: Joshua Jun <lets@throw.rocks>
Round scaled intensity upward so each preset reaches a distinct hardware step.

Retain the board's 25 percent ceiling.

Fixes FIRM-3337

Co-authored-by: GPT-5 Codex <codex@openai.com>
Signed-off-by: Joshua Jun <lets@throw.rocks>
Allow boards to select percentage-based warning points.

Retain the existing time-based defaults for other boards.

Configure Getafix warnings at 12 and 8 percent.

Fixes FIRM-3325

Co-authored-by: GPT-5 Codex <codex@openai.com>
Signed-off-by: Joshua Jun <lets@throw.rocks>
Narrow the LIS2DW12 wake threshold range and lower its default threshold.

Keep the existing Low, Medium, and High preference values unchanged.

Fixes FIRM-2573

Co-authored-by: GPT-5 Codex <codex@openai.com>
Signed-off-by: Joshua Jun <lets@throw.rocks>
Replace the full 1792-line copy with an @include of the base Doxyfile
plus the nine settings it overrides. Verified with doxygen -x_noenv that
the effective configuration is byte-identical to the previous file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
Remove the fifteen tags that modern doxygen flags as obsolete
(TCL_SUBST, SYMBOL_CACHE_SIZE, COLS_IN_ALPHA_INDEX, HTML_TIMESTAMP,
FORMULA_TRANSPARENT, LATEX_SOURCE_CODE, XML_SCHEMA, XML_DTD, PERL_PATH,
CLASS_DIAGRAMS, MSCGEN_PATH, DOT_FONTNAME, DOT_FONTSIZE,
DOT_TRANSPARENT, DOT_MULTI_TARGETS) and rename the project from the
old tintin-fw codename to PebbleOS.

Only behavior change: the obsolete HTML_TIMESTAMP=YES no longer maps to
TIMESTAMP=YES, so generated pages lose the embedded timestamp, which
also makes doc builds reproducible. Everything else verified identical
via doxygen -x_noenv before/after.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
Kconfig options are documented as tweakable via menuconfig, but pbl did
not forward the command, forcing a drop back to ./waf for that one step.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
The development docs mixed ./waf and ./pbl invocations, sometimes on
the same page. Standardize on ./pbl, which forwards all the referenced
commands (configure, menuconfig, build, bundle) to waf.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
The two-line OpenOCD gdb script is referenced nowhere; the openocd
runner uses .gdbinit instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
iOS creates connections with a 720ms supervision timeout and only
relaxes it once the peripheral requests new parameters. We waited the
full TGAP(conn_pause_peripheral) = 5s before sending the request, so
any RF fade during that window killed the link. Logs from a getafix
unit showed reconnect storms where most connections died of supervision
timeout (0x208) within 1-6s of connecting, i.e. before the update could
be requested, while connections that reached the 6s supervision timeout
stayed up for hours.

Shorten the initial pause to 1s. The 5s pause is a spec recommendation
("should", not "shall"), and Apple has confirmed relaxed handling of
TGAP timings (see the pre-existing note about the 30s Tgap timeout).

Related to FIRM-3330.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
When the link repeatedly drops right after connecting (e.g. supervision
timeouts in bad RF conditions), every disconnection restarted reconnect
advertising with a 30s short-interval (20ms) burst. A getafix unit
hitting a reconnect storm went through ~130 connect/disconnect cycles
in 70 minutes, each paying the full burst.

Track how often plain reconnect advertising is (re)started: after 3
starts within a 60s window, schedule only the long-interval (~1s) term.
One attempt per window still gets the short-interval burst, so
reconnection latency recovers as soon as the churn stops. BLE HRM
reconnection advertising is user-initiated and time-bounded, so it is
not backed off.

Related to FIRM-3330.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
With menu scroll wrap-around enabled and automatic timezone source,
pressing up from the top of the Date & Time menu wrapped the selection
onto the non-selectable Timezone row; selecting it hit
PBL_ASSERTN(clock_timezone_source_is_manual()) and rebooted the watch.

Two changes:

- The menu layer wrap-around path called menu_layer_set_selected_index()
  directly, which skips the selection_will_change callback, so wrap-around
  could land on rows the callback would have redirected away from. Run the
  callback on the wrap destination like normal scrolling does, and cancel
  the wrap if the callback locks the selection in place. Covered by two
  new unit tests.

- Per review feedback on an earlier attempt (coredevices#1543), a visible but
  non-selectable row is confusing, so hide the Timezone row entirely while
  the timezone source is automatic, mirroring how Set Time and Set Date
  are hidden for the automatic time source. The active region is still
  shown as the Timezone Source subtitle, and the assert is replaced with
  a plain guard so selecting the row can never reboot the watch.

Fixes coredevices#1521

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
The .kernel_heap section used to advance the location counter to the
end of KERNEL_RAM, so the linker's --print-memory-usage report always
showed the region as 100% used. Keep the section empty and assign
_heap_end outside of it instead: the report now shows only the
statically allocated portion (data, bss and kernel stacks), with the
remainder implicitly available as kernel heap. The _heap_start and
_heap_end symbols are unchanged.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
Remove twelve packages with no remaining users: ply, pyusb, GitPython,
pycryptodome, mock, nose, boto, enum34, pep8, protobuf (transitive dep
of nanopb), certifi (transitive dep of requests) and pathlib. Several
are Python-2-era relics: pathlib and enum34 are stdlib backports that
can shadow the standard library, nose cannot even be imported on
Python >= 3.12, and the GitPython pin predated the CVE-2022-24439 fix.
The only nose user was an inline self-test in extract_comments.py,
now using plain asserts.

Also pin sh<2: the waf tooling uses the sh 1.x API and a fresh install
today resolves sh 2.x, which has breaking API changes. Raise the
prompt_toolkit floor to the 3.x line actually in use, unpin cobs
(pulse2's own dependency is unpinned) and group the file by purpose.

Verified with a fresh venv: pip install succeeds (72 packages),
./pbl configure && ./pbl build (qemu_emery) both pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
The legacy directory was created as a staging area with the intent to
eventually bring the content onto the docs platform. The PULSEv2
protocol specs match the live implementation (src/fw/console/pulse*,
tools/pulse/), so move the four pages into the built documentation
under reference/.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
Remove the five files not worth promoting: battery_state.dot describes
a ten-state charge state machine that no longer exists (the current
battery_state.c has three connection states), legacy_flash_imaging.txt
is self-declared obsolete and superseded by PULSE flash imaging,
docstring-help.md points at paths that moved to sdk/docs/ years ago,
tasks.txt predates the current PebbleTask_* naming and cites removed
APIs, and porting-bluetooth.md documents the bt_driver layer that is
slated for removal. With the legacy directory gone, drop it from the
Sphinx exclude list.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
The timelineQuickViewWatchfaceFit pref can be set on a watch that
supports it (obelix), sync to the phone, and then be pushed to a watch
that does not (getafix): the settings BlobDB whitelist accepted the key
on every board, so the record was stored even though the board has no
watchface fit support.

Reject the key at the BlobDB whitelist on boards without
TIMELINE_PEEK_WATCHFACE_FIT_SUPPORTED, and delete any record already
stored by earlier firmware during shell_prefs_init, so upgrading to
this version discards the stale pref.

Verified on qemu_gabbro: a record inserted by the previous firmware is
discarded at first boot ("Discarding unsupported pref"), and new
phone-side inserts are rejected ("Rejecting non-whitelisted setting").

Fixes FIRM-3391

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Eric Migicovsky <ericmigi@gmail.com>
Gate the release workflow's AWS log hash dictionary upload, Memfault
symbol upload and Memfault release deployment on the public
coredevices/PebbleOS repository, matching the existing condition used
by the build workflows. This prevents CI in private forks from
attempting these uploads.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
Tags like v4.9.142.1 were rejected by the tag validation regex, aborting
release builds (see failed Release run for tag v4.9.142.1). Allow an
optional fourth numeric component in the tag. It is exposed through TAG
and folded into PATCH_VERBOSE_STRING (e.g. 142.1-0), while
MAJOR/MINOR/PATCH and MAJOR_MINOR_PATCH_STRING keep their three-component
form. Parsing of all previously accepted tag forms is unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
Introduce a Zephyr-like top-level subsys/ folder for OS subsystems.
Move the core logging implementation (log formatting/dispatch and the
PULSE logging backend) out of src/fw/kernel into subsys/logging,
together with its Kconfig menu and the per-module log level template.

The subsys/ directory is exported as an include path, so subsystem
headers are included as logging/<header>.h. The moddable submodule
needs a matching include path update.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
Move the PBL_LOG public header from src/fw/system/logging.h to
include/pbl/logging/logging.h, and move the existing logging headers
(binary_logging.h, log_hashing.h) from include/logging to the same
place, following the pbl/ include prefix convention. All in-tree users
now include <pbl/logging/...>. The moddable submodule needs a matching
include path update.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
prv_get_notification() fills the TimelineItem via
timeline_item_deserialize_item(), which allocates allocated_buffer from
the calling task's heap, but prv_compress() freed it with kernel_free().
Compression can run on the app task, e.g. when the workout summary
notification is stored while notification storage is full, so an
app-heap pointer hit the kernel heap's range assert (heap.c:275) and
rebooted the watch.

Free the buffer with timeline_item_free_allocated_buffer() so the free
targets the same heap as the allocation. On kernel tasks this is
equivalent to the old behavior.

Fixes FIRM-3485
Refs FIRM-1925

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Joshua Jun <lets@throw.rocks>
notification_storage_rewrite() never freed the TimelineItem buffer that
prv_rewrite_iter_next() allocates for every live entry, leaking one
buffer per notification on each rewrite (e.g. timezone migration).

Freeing per iteration exposes the deleted-entry path, which was broken
in two ways: the iterator did not seek past a deleted entry's payload,
so the next header was read from payload bytes and iteration ended
early; and the loop still wrote a record for the deleted entry, using
the previous item's stale payload. Skip deleted entries entirely,
matching prv_compress(), and track the bytes written so s_write_offset
stays correct for the now-shorter file.

Add a unit test covering a rewrite over live and deleted entries; it
fails against the previous code and passes now.

Refs FIRM-3485

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Joshua Jun <lets@throw.rocks>
The AUDCODEC DMA half/full-complete ISRs enqueued a prv_audio_trans_bg
refill callback on every interrupt with no in-flight tracking. When
KernelBG is starved (e.g. an app blocking on a slow flash resource read
while holding the pfs mutexes), the system task queue fills up and the
watch hard-reboots with Event Queue Full, cutting alarm tones off a few
seconds into playback.

Mirror the nrf5/da7212 driver: track a callback_pending flag so at most
one refill callback is in flight, clearing it again if the enqueue
fails. The flag is also reset in audec_start() so a stale value cannot
block refills on a new session.

Fixes FIRM-3470
Refs FIRM-2438

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Joshua Jun <lets@throw.rocks>
Some non-Apple AMS servers format the PlaybackInfo floats using a
comma decimal separator (e.g. "0,1,00,-0,00" from a de_DE Android
phone). The CSV parser splits on every comma, so the per-field
callback gets called with idx >= 3, performing an out-of-bounds read
of multiplier[] and then hitting the WTF assert. The phone re-sends
the poisoned value seconds after every reconnect while music plays,
so the watch crash-loops; three crashes with less than 15 minutes of
uptime each make the bootloader fall back to PRF, which invalidates
both firmware slots and forces the user to reinstall daily.

Bound-check the field index and stop parsing instead of asserting;
the update is then rejected by the existing field-count check.

Fixes FIRM-3228
Fixes FIRM-2706
Fixes FIRM-3441

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Joshua Jun <lets@throw.rocks>
rhaist and others added 28 commits July 22, 2026 14:38
The parameter was declared as weighting_factory_inverted in the header,
while the implementation and struct field already use
weighting_factor_inverted. Rename the declaration to match.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
pbl_std.h re-declared time_ms with a different parameter name (t_utc vs
tloc) while already including util/time/time.h, which holds the
canonical declaration. Drop the duplicate and move the full parameter
docs to time.h, whose comment previously deferred to pbl_std.h.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
Move all driver headers from src/fw/drivers to include/pbl/drivers,
following the pbl/ include prefix convention. All in-tree users now
include <pbl/drivers/...>. Update the third_party glue code (FreeRTOS
config, memfault and nimble ports) and bump the moddable submodule
with a matching include path fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
factory_reset() only set BOOT_BIT_FORCE_PRF and invalidated the
firmware slots after pfs_format(), which holds the PFS mutex across a
multi-minute flash erase. Any reset inside that window (task watchdog
on a task blocked on the PFS mutex, crash, power loss) rebooted the
watch straight back into normal firmware: pairing was already wiped
and the half-erased filesystem was silently reformatted on boot, so
the watch came up unpaired as if the factory reset never happened.

Set the boot bit and invalidate the slots right after the non-PFS
teardown, before starting the format, so an interrupted factory reset
lands in PRF, which is the expected outcome. A partially erased
filesystem is reformatted on first use anyway.

Fixes FIRM-3549

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Joshua Jun <lets@throw.rocks>
Merge the 'adding a new SDK function' checklist from AGENTS.md, the
generator/exported_symbols.json documentation from
tools/generate_native_sdk/README.md, and the sdk/ assembly notes from
sdk/readme.txt into one canonical page at docs/development/sdk_export.md.

The generator README and sdk/readme.txt are removed: their content is
absorbed by the new page, and readme.txt referenced a src_readme.txt
that no longer exists.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
Include the root CONTRIBUTING.md (DCO, commit rules, generative-AI
policy) as a docs page — it was previously only discoverable on GitHub —
and add the formatting and comment-style conventions that so far only
lived in AGENTS.md. Point CONTRIBUTING.md at the published docs so
contributors can find the build, QEMU and SDK export guides.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
Move the operational detail that only existed in the working-with-qemu
agent skill (monitor sockets, serial ports, uart1.log, screenshots,
programmatic sendkey input) into docs/development/qemu.md. The skill
becomes a pointer to the page plus agent-specific notes, following the
docs-replace-skills direction discussed in coredevices#1775.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
Replace the inline SDK-function procedure with a trigger line linking
docs/development/sdk_export.md, and add a Documentation section pointing
agents at the contributor pages instead of duplicating them here.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
- moddable.md pointed at pebble-dev/moddable, which no longer exists;
  the integration submodule lives at coredevices/moddable. Also fix the
  'runningJavaScript' typo.
- pulse2.md linked the retired pebble/pulse2 repository; the Python
  implementation now lives in-repo at tools/libs/pulse2.
- Drop the pulse2-wireshark-plugin link: the repository is gone from
  GitHub entirely.
- Normalize the RST-era tables to Markdown pipe tables and rejoin two
  data rows that the original RST-to-Markdown conversion had left
  rendering as code blocks.

Found by sphinx-build -b linkcheck.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
Run sphinx-build -b linkcheck on a weekly schedule (plus manual
dispatch) so external link rot is caught mechanically. Scheduled rather
than PR-gating so transient third-party outages don't block PRs.
reveng.sourceforge.net is ignored: alive, but blocks automated requests.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
Verified every claim in the development and board pages against the
current tree. Fixes:

- options.md: CONFIG_NOSTOP and CONFIG_NOSLEEP do not exist in any
  Kconfig (pre-Kconfig relics); the default log level is INFO, not
  DEBUG (subsys/logging/Kconfig defaults DEFAULT_LOG_LEVEL_INFO).
- getting_started.md: add nodejs to the Ubuntu list (hard configure
  requirement); bring the Fedora list to parity with Ubuntu (gettext,
  git, make, openocd, bison, flex, gperf, ncurses-devel — all names
  verified against Fedora 44; gcc-multilib has no equivalent since
  nothing builds 32-bit; python3-venv ships with Fedora's python3);
  use Fedora's real python3-devel package name; install the checked-in
  requirements-brew.txt on macOS instead of a diverging inline list.
- asterix: use the current product name Pebble 2 Duo (as in options.md
  and the qemu tooling), keeping the original launch name Core 2 Duo
  as an alias; grammar fix.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
sdk/docs/mainpage_sdk.dox has been zero bytes since the original 2013
import and only ever appeared in the SDK Doxyfile INPUT list; drop both.

tools/libs/pbl/README.md still described the package as a standalone
private Pebble repository (dead git+ssh install URL, references to
pebble-tool-dev and past maintainers); rewrite it to describe the
in-repo package as it exists today.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
Remove the Power Mode (high performance / low power) shell preference,
its Debugging settings menu entry, the powermode service usages in the
app manager, modal manager and firmware update service, and the
settings_power_mode analytics metric. Bump the native heartbeat record
version since the packed record layout changed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
The powermode service has no users left, drop it along with its test
stub.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
The cpumode driver has no users left after the powermode service
removal, drop it and its board configuration entries.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
src/fw/services/activity/docs/index.md (~30 KB on step counting and
sleep tracking algorithms, plus 7 figures) was a standalone document not
referenced from anywhere in the tree and invisible on the docs site.
Move the directory wholesale to docs/architecture/activity/ — a pure
rename, so the relative image references keep working — and list it
under a new Architecture section.

The file references it makes (health_service.c, kraepelin algorithm
files, tools/activity scripts, test fixtures) were verified to still
exist in the current tree.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
A link-first tour of the firmware: layering, task model, boot and PRF
variants, app identity and shell flow, memory layout, BLE stack,
filesystem, driver splits and coredumps. The in-source design comments
remain canonical; the page summarizes and points at them rather than
duplicating.

Every referenced path and claim was verified against the current tree:
notably the accel driver interface now lives in include/pbl/drivers/,
the bootloader is external to this repository (normal vs PRF is a
build-time variant), and NimBLE is the transport backend beneath the
still-live src/fw/comm/ble host layer on all current boards.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robert Haist <rhaist@mailbox.org>
Remove stub driver sources that are no longer referenced by any build
script. Only the ambient light, EXTI, MCU, OTP and watchdog stubs
remain in use.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
__assert_func is the assert() hook of the newlib-family headers and is
needed for every libc selection: the toolchain libcs call it from their
own assert(), and even pblibc builds compile third-party code against
the toolchain's <assert.h> since pblibc ships no assert header. It is
libc glue, not firmware code, so move it from src/fw (where the source
glob picked it up) to lib/c and link it explicitly via LIBC_USE like
the other libc override objects.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
Picolibc gives the smallest footprint with complete C99 formatted I/O
and ships pre-built with the PebbleOS SDK toolchain, so make it the
default C library instead of the in-tree pblibc.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
All boards now build against picolibc (or newlib) from the toolchain,
so drop the hand-written pblibc and everything that existed only to
support it: its sources and headers (lib/c/pebble), the LIBC_PEBBLE
Kconfig option and configure branch, the pblibc/pblibc_includes use
targets and their empty shims (including the stale references in the
third_party wscripts and moddable's CONFIG_LIBC_PEBBLE qsort/bsearch
branch), the __aeabi_* shims in src/fw/system/syscalls.c that -nostdlib
required, and the host unit tests exercising pblibc code (tests/libc).
The strftime test is kept: it exercises the firmware pbl_std
implementation, not pblibc, so it moves to tests/fw/applib. The
libc_syscalls _sbrk override is now built unconditionally since a
toolchain libc is always selected.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
Triaging MOB-10077 suggested this as a possibility - add log to know in future.

Signed-off-by: Steve Penna <steve@penna.org.uk>
Seventy-eight upstream commits, the largest sync since the fork
reconciled: picolibc becomes the default libc (in-tree libc removed;
meson and ninja are new build dependencies), NimBLE updated to 1.10.0
with Mbed TLS 3.6.7 as a new submodule, fresh SiFli HAL and LCPU patch,
public headers moved under include/pbl (drivers, logging), the
power-mode/cpumode subsystem removed, reconnect advertising backoff
during connection churn, sooner post-connect parameter updates, speaker
volume rework, lsm6dso FIFO burst reads, PRF committed before factory
reset wipes, and a docs overhaul.

Reconciliation notes:
- Debugging menu keeps all fork rows; the Power Mode row is dropped
  with upstream's subsystem removal (the fork's governor was already
  inert at upstream baseline).
- gap_le_slave_reconnect: composed the fork's dual-phone term selection
  (long-interval-only while a phone is connected) with upstream's churn
  backoff, which applies only to cold reconnects.
- ppogatt: upstream's new alloc-failure log adapted from their
  transport role field to the fork's phone slot.
- reversed-PPoG service and header stay removed (fork policy).
- NimBLE submodule: fork pin carried zero fork commits, so the 1.10.0
  bump is a plain fast-forward; same for the HAL.
- Fork-only files updated for the header moves (logging, rtc).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Coral Coder <noreply@anthropic.com>
Fixes FIRM-2117

Signed-off-by: Joshua Jun <lets@throw.rocks>
Signed-off-by: Joshua Jun <lets@throw.rocks>
Signed-off-by: Joshua Jun <lets@throw.rocks>
Health can now be quick launched with Down, but its open and close
animations were hardcoded for the Up entry (shutter Up on open, plain
push Down on close).

Open: thread the launch config into shell_get_open_compositor_animation
and, for Down launches, enter with a true vertical push. The new
compositor_slide_transition_app_get reuses the timeline slide machinery
to draw the incoming app framebuffer sliding up from the bottom while
the watchface is pushed out the top, matching the reversed card
navigation inside the app. A mirrored shutter was tried first, but a
41 px nudge plus a wedge growing from the top cannot read as a bottom
entry.

Close: derive the exit direction from the closing app's launch info in
its ProcessContext, so a Down-launched Health exits upwards with the
wedge shutter. The Down shutter direction gains a mirrored wedge path
definition and an upward settle, making the Up-launched exit the exact
mirror of the Down-launched one; movement size, sign and wedge drawing
now derive from the transition direction itself. Up, Right and Left
shutters render exactly as before.

Verified frame by frame in QEMU (qemu_emery).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Joshua Jun <lets@throw.rocks>
Four quick-launch and health navigation commits, plus upstream's
v4.27.0 through v4.31.0 release tags cut from already-merged history.

Signed-off-by: Coral Coder <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@Coral-coder
Coral-coder marked this pull request as ready for review July 23, 2026 14:30
@Coral-coder
Coral-coder merged commit a480daf into main Jul 23, 2026
34 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants